<?php
/**
 * Zhub - A PHP Framework For API Developer
 *
 * This file is part of the Zhub package.
 *
 * (c) YiLong Fan <fantiq@163.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @author fanyilong <fantiq@163.com>
 * @copyright 2017 fanyilong. All rights reserved.
 * @package Zhub
 * @date 18-3-2
 */

namespace Zhub\CodeGenerator\Struct;


use Zhub\Console\Command;
use Zhub\Console\ConsoleKernel;
use Zhub\Console\Output;
use Zhub\DataStruct\Struct;
use Zhub\Prototype\Objective\Clazz\Clazz;
use Zhub\Prototype\Objective\Clazz\ClazzUtil;
use Zhub\Prototype\Objective\Clazz\Constant\ConstantStruct;
use Zhub\Prototype\Objective\Clazz\Method\ArgumentMap;
use Zhub\Prototype\Objective\Clazz\Method\ArgumentStruct;
use Zhub\Prototype\Objective\Clazz\Method\MethodStruct;
use Zhub\Prototype\Variable\Type;
use Zhub\Prototype\Variable\TypeEnum;
use Zhub\Utils\Str;

class MakeStructCommand extends Command
{
    private $console = null;

    public static function name(): string
    {
        return 'make:struct';
    }

    public static function description(): string
    {
        return <<<DESC
to make a struct ,use like this
php console.php make:struct <fullClassName> <attribute=defaultValue>
DESC;

    }

    public function run(ConsoleKernel $console):Output
    {
        $this->console = $console;

        $this->createClazz($console->getArguments());
        $output = new Output();
        $output->success("ok");
        return $output;
    }

    public function createClazz(array $arguments)
    {
        $fullClassName = array_shift($arguments);
        list($className, $nameSpace) = ClazzUtil::parseClassNameSpace($fullClassName);
        $clazz = new Clazz($nameSpace, $className);
        $clazz->setExtends(Struct::class);
        $methodMap = $clazz->getMethods();
        $constantMap = $clazz->getConstants();

        $initializeConst = null;
        foreach ($arguments as $argument) {
            list($name, $val) = explode('=', $argument);
            $constName = strtoupper($name);
            $methodName = Str::toUpperCamelCase($name);
            $paramName = Str::toLowerCamelCase($name);

            $constantMap->put($constName, new ConstantStruct([
                ConstantStruct::NAME => $constName,
                ConstantStruct::DEFAULT_VALUE => $paramName,
            ]));
            $type = Type::newByValue($val);
            $methodMap->put('get' . $methodName, new MethodStruct([
                MethodStruct::NAME => 'get' . $methodName,
                MethodStruct::RETURN_TYPE => $type,
                MethodStruct::CONTENT => 'return $this->get(self::' . $constName . ');',
            ]));

            $paramMap = new ArgumentMap();
            $paramMap->put($paramName, new ArgumentStruct([
                ArgumentStruct::NAME => $paramName,
                ArgumentStruct::TYPE => $type,
            ]));
            $methodMap->put('set' . $methodName, new MethodStruct([
                MethodStruct::NAME => 'set' . $methodName,
                MethodStruct::PARAMETERS => $paramMap,
                MethodStruct::CONTENT => "\$this->set(self::$constName, \$$paramName);",
            ]));

            if ($type->getId() == TypeEnum::CLAZZ) {
                list($clazzName) = ClazzUtil::parseClassNameSpace($type->getValue());
                $value = "function () {return $clazzName::class;}";
            }
            else {
                $value = $val;
                $valType = Type::newByValue($val);
                if ($valType->getId() != TypeEnum::STRING) {
                    $value = trim($val, '\'"');
                }
            }
            $initializeConst .= "\t\tself::$constName => " . $value . ',' . PHP_EOL;
        }

        $initializeContent = sprintf("return [" . PHP_EOL . '%s' . PHP_EOL . "\t];" , $initializeConst);

        $methodMap->put('initialize', new MethodStruct([
            MethodStruct::STATIC => true,
            MethodStruct::RETURN_TYPE => Type::newByTypeId(TypeEnum::ARRAY),
            MethodStruct::NAME => 'initialize',
            MethodStruct::CONTENT => $initializeContent,
        ]));
        $this->createFile($nameSpace, $className, $clazz->toString());
    }

    private function createFile(string $nameSpace, string $className, string $contents)
    {
        $path = trim(
            str_replace($this->console->rootNameSpace, '', $nameSpace),
            ClazzUtil::CLASS_NAME_SPACE_SEPARATOR
        );
        $filePath = $this->console->rootPath;
        foreach (explode(ClazzUtil::CLASS_NAME_SPACE_SEPARATOR, $path) as $segment) {
            $filePath .= DIRECTORY_SEPARATOR . $segment;
            if (!is_dir($filePath)) {
                var_dump($filePath);
                mkdir($filePath, 0755);
            }
        }
        $filePath .= DIRECTORY_SEPARATOR . $className . '.php';
        $fp = fopen($filePath, 'w');
        return fwrite($fp, $contents);
    }
}